/*
 * @(#)$Id$
 *
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 2001, Sun
 * Microsystems., http://www.sun.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * @author Jacek Ambroziak
 * @author Santiago Pericas-Geertsen
 * @author Morten Jorgensen
 *
 */

package org.apache.xalan.xsltc.compiler;

import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.net.URL;

import javax.xml.parsers.*;

import org.xml.sax.*;

import org.apache.xalan.xsltc.compiler.util.Type;

import de.fub.bytecode.generic.*;
import de.fub.bytecode.classfile.JavaClass;

import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
import org.apache.xalan.xsltc.compiler.util.Util;

import org.apache.xalan.xsltc.DOM;

public final class Stylesheet extends SyntaxTreeNode {
    private String       _version;

    private QName        _name;
    private URL          _url;
    private Stylesheet   _parentStylesheet;
	
    // Contains global variables and parameters defined in the stylesheet
    private final Vector _globals = new Vector();

    // Used to cache the result returned by <code>hasLocalParams()</code>.
    private Boolean _hasLocalParams = null;

    //  The name of the class being generated.
    private String _className;
    
    // Contains all templates dedined in this stylesheet
    private final Vector _templates = new Vector();

    private int _nextModeSerial = 1;
    private final Hashtable _modes = new Hashtable();
    private final Hashtable _extensions = new Hashtable();

    private int _importPrecedence = 1;
    private Mode _defaultMode;
    private boolean _multiDocument = false;

    // All named key elements (needed by Key/IdPattern)
    private Hashtable _keys = new Hashtable();

    private boolean _numberFormattingUsed = false;

    private boolean _simplified = false;

    public boolean isSimplified() {
	return(_simplified);
    }

    public void setSimplified() {
	_simplified = true;
    }
    
    public void setMultiDocument(boolean flag) {	
	_multiDocument = flag;
    }

    public boolean isMultiDOM() {
	return _multiDocument;
    }

    public boolean isImported() {
	final SyntaxTreeNode parent = getParent();
	return ((parent != null) && (parent instanceof Import));
    }

    public boolean isIncluded() {
	final SyntaxTreeNode parent = getParent();
	return ((parent != null) && (parent instanceof Include));
    }

    public void numberFormattingUsed() {
	_numberFormattingUsed = true;
    }

    public void setImportPrecedence(final int precedence) {
	_importPrecedence = precedence;

	final Enumeration elements = elements();
	while (elements.hasMoreElements()) {
	    final TopLevelElement child =
		(TopLevelElement)elements.nextElement();
	    if (child instanceof Include) {
		Stylesheet included = ((Include)child).getIncludedStylesheet();
		if (included != null) {
		    included.setImportPrecedence(precedence);
		}
	    }
	}

    }
    
    public int getImportPrecedence() {
	return _importPrecedence;
    }

    public boolean checkForLoop(URL url) {
	return _url.sameFile(url) ||
	    _parentStylesheet != null && _parentStylesheet.checkForLoop(url);
    }
    
    public void setParser(Parser parser) {
	super.setParser(parser);
	_name = makeStylesheetName("%stylesheet%");
    }
    
    public void setParentStylesheet(Stylesheet parent) {
	_parentStylesheet = parent;
    }
    
    public Stylesheet getParentStylesheet() {
	return _parentStylesheet;
    }

    public void setURL(URL url) {
	_url = url;
    }
    
    public URL getURL() {
	return _url;
    }

    private QName makeStylesheetName(String prefix) {
	return getParser().getQName(prefix+getXSLTC().nextStylesheetSerial());
    }

    /**
     * Returns true if this stylesheet has global vars or params.
     */
    public boolean hasGlobals() {
	return _globals.size() > 0;
    }

    /**
     * Returns true if at least one template in the stylesheet has params
     * defined. Uses the variable <code>_hasLocalParams</code> to cache the
     * result.
     */
    public boolean hasLocalParams() {
	if (_hasLocalParams == null) {
	    final int n = _templates.size();
	    for (int i = 0; i < n; i++) {
		final Template template = (Template)_templates.elementAt(i);
		if (template.hasParams()) {
		    _hasLocalParams = new Boolean(true);
		    return true;
		}
	    }
	    _hasLocalParams = new Boolean(false);
	    return false;
	}
	else {
	    return _hasLocalParams.booleanValue();
	}
    }

    /**
     * Store extension URIs
     */
    private void extensionURI(String prefixes, SymbolTable stable) {
	if (prefixes != null) {
	    StringTokenizer tokens = new StringTokenizer(prefixes);
	    while (tokens.hasMoreTokens()) {
		final String prefix = tokens.nextToken();
		final String uri = lookupNamespace(prefix);
		if (uri != null) {
		    _extensions.put(uri, prefix);
		}
	    }
	}
    }

    public boolean isExtension(String uri) {
	return (_extensions.get(uri) != null);
    }

    public void excludeExtensionPrefixes(Parser parser) {
	final SymbolTable stable = parser.getSymbolTable();
    	final String excludePrefixes = getAttribute("exclude-result-prefixes");
	final String extensionPrefixes = 
	    getAttribute("extension-element-prefixes");
	stable.excludeNamespaces(excludePrefixes);
	stable.excludeNamespaces(extensionPrefixes);
	extensionURI(extensionPrefixes, stable);
    }

    /**
     * Parse the version and uri fields of the stylesheet and add an
     * entry to the symbol table mapping the name <tt>%stylesheet%</tt>
     * to an instance of this class.
     */
    public void parseContents(Parser parser) {
	final SymbolTable stable = parser.getSymbolTable();

	_version = getAttribute("version");

	addPrefixMapping("xml", "xml"); // Make sure 'xml' maps to 'xml'

	// Report and error if more than one stylesheet defined
	final Stylesheet sheet = stable.addStylesheet(_name, this);
	if (sheet != null) {
	    // Error: more that one stylesheet defined
	    final ErrorMsg msg = new ErrorMsg(ErrorMsg.STLREDEF_ERR, this);
	    parser.reportError(Constants.ERROR, msg);
	}

	// If this is a simplified stylesheet we must create a template that
	// grabs the root node of the input doc ( <xsl:template match="/"/> ).
	// This template needs the current element (the one passed to this
	// method) as its only child, so the Template class has a special
	// method that handles this (parseSimplified()).
	if (_simplified) {
	    stable.excludeURI(XSLT_URI);
	    Template template = new Template();
	    template.parseSimplified(this, parser);
	}
	// Parse the children of this node
	else {
	    parseOwnChildren(parser);
	}
    }

    /**
     * Parse all the children of <tt>element</tt>.
     * XSLT commands are recognized by the XSLT namespace
     */
    public final void parseOwnChildren(Parser parser) {

	final Vector contents = getContents();
	final int count = contents.size();

	// We have to scan the stylesheet element's top-level elements for
	// variables and/or parameters before we parse the other elements...
	for (int i=0; i<count; i++) {
	    SyntaxTreeNode child = (SyntaxTreeNode)contents.elementAt(i);
	    if ((child instanceof Param) || (child instanceof Variable)) {
		parser.getSymbolTable().setCurrentNode(child);
		child.parseContents(parser);
	    }
	}

	// Now go through all the other top-level elements...
	for (int i=0; i<count; i++) {
	    SyntaxTreeNode child = (SyntaxTreeNode)contents.elementAt(i);
	    if (!((child instanceof Param) || (child instanceof Variable))) {
		parser.getSymbolTable().setCurrentNode(child);
		child.parseContents(parser);
	    }
	}
    }

    public void processModes() {
	if (_defaultMode == null)
	    _defaultMode = new Mode(null, this, Constants.EMPTYSTRING);
	_defaultMode.processPatterns(_keys);
	final Enumeration modes = _modes.elements();
	while (modes.hasMoreElements()) {
	    final Mode mode = (Mode)modes.nextElement();
	    mode.processPatterns(_keys);
	}
    }
	
    private void compileModes(ClassGenerator classGen) {
	_defaultMode.compileApplyTemplates(classGen);
	final Enumeration modes = _modes.elements();
	while (modes.hasMoreElements()) {
	    final Mode mode = (Mode)modes.nextElement();
	    mode.compileApplyTemplates(classGen);
	}
    }

    public Mode getMode(QName modeName) {
	if (modeName == null) {
	    if (_defaultMode == null) {
		_defaultMode = new Mode(null, this, Constants.EMPTYSTRING);
	    }
	    return _defaultMode;
	}
	else {
	    Mode mode = (Mode)_modes.get(modeName);
	    if (mode == null) {
		final String suffix = Integer.toString(_nextModeSerial++);
		_modes.put(modeName, mode = new Mode(modeName, this, suffix));
	    }
	    return mode;
	}
    }

    /**
     * Type check all the children of this node.
     */
    public Type typeCheck(SymbolTable stable) throws TypeCheckError {
	return typeCheckContents(stable);
    }

    /**
     * Translate the stylesheet into JVM bytecodes. 
     */
    public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
	translate();
    }

    private void addDOMField(ClassGenerator classGen) {
	final FieldGen fgen =
	    new FieldGen(ACC_PUBLIC,
			 Util.getJCRefType(classGen.getDOMClassSig()),
			 DOM_FIELD,
			 classGen.getConstantPool());
	
	classGen.addField(fgen.getField());
    }

    /**
     * Translate the stylesheet into JVM bytecodes. 
     */
    public void translate() {
	Output lastOutputElement = null;
	_className = getXSLTC().getClassName();

	// Define a new class by extending TRANSLET_CLASS
	final ClassGenerator classGen =
	    new ClassGenerator(_className,
			       TRANSLET_CLASS,
			       Constants.EMPTYSTRING,
			       ACC_PUBLIC | ACC_SUPER,
			       null, this);
	
	addDOMField(classGen);

	// Compile a default constructor to init the namesIndex table
	//compileConstructor(classGen);

	// Compile transform() to initialize parameters, globals & output
	// and run the transformation
	compileTransform(classGen);

	// Translate all non-template elements and filter out all templates
	final Enumeration elements = elements();
	while (elements.hasMoreElements()) {
	    Object element = elements.nextElement();
	    // xsl:template
	    if (element instanceof Template) {
		// Separate templates by modes
		final Template template = (Template)element;
		_templates.addElement(template);
		getMode(template.getModeName()).addTemplate(template);
	    }
	    // xsl:attribute-set
	    else if (element instanceof AttributeSet) {
		((AttributeSet)element).translate(classGen, null);
	    }
	    else if (element instanceof Output) {
		// save the element for later to pass to compileConstructor 
		lastOutputElement = (Output)element;
	    }
	    else {
		// Global variables and parameters are handled elsewhere.
		// Other top-level non-template elements are ignored. Literal
		// elements outside of templates will never be output.
	    }
	}

	processModes();
	compileModes(classGen);

	compileConstructor(classGen, lastOutputElement);

	if (!getParser().errorsFound()) {
	    getXSLTC().dumpClass(classGen.getJavaClass());
	}
    }
	
    private void compileConstructor(ClassGenerator classGen, Output output) {

	final ConstantPoolGen cpg = classGen.getConstantPool();
	final InstructionList il = new InstructionList();
	il.append(classGen.loadTranslet());
	il.append(new INVOKESPECIAL(cpg.addMethodref(TRANSLET_CLASS,
						     "<init>", "()V")));

	final Vector names = getXSLTC().getNamesIndex();
	il.append(classGen.loadTranslet());
	il.append(new PUSH(cpg, names.size()));
	il.append(new ANEWARRAY(cpg.addClass(STRING)));		

	for (int i = 0; i < names.size(); i++) {
	    final String name = (String)names.elementAt(i);
	    il.append(DUP);
	    il.append(new PUSH(cpg, i));
	    il.append(new PUSH(cpg, name));
	    il.append(AASTORE);
	}
	il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS,
					       NAMES_INDEX,
					       NAMES_INDEX_SIG)));

	final Vector namespaces = getXSLTC().getNamespaceIndex();
	il.append(classGen.loadTranslet());
	il.append(new PUSH(cpg, namespaces.size()));
	il.append(new ANEWARRAY(cpg.addClass(STRING)));		

	for (int i = 0; i < namespaces.size(); i++) {
	    final String ns = (String)namespaces.elementAt(i);
	    il.append(DUP);
	    il.append(new PUSH(cpg, i));
	    il.append(new PUSH(cpg, ns));
	    il.append(AASTORE);
	}
	il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS,
					       NAMESPACE_INDEX,
					       NAMESPACE_INDEX_SIG)));

	// Introduces bytecodes to set encoding from <xsl:output> if exists. 
	if (output != null) {
	    output.translateEncoding(classGen, il);
	}

	final MethodGenerator constructor =
	    new MethodGenerator(ACC_PUBLIC,
				de.fub.bytecode.generic.Type.VOID, 
				null, null, "<init>", 
				_className, il, cpg);
	
	// Compile default decimal formatting symbols.
	// This is an implicit, nameless xsl:decimal-format top-level element.
	if (_numberFormattingUsed)
	    DecimalFormatting.translateDefaultDFS(classGen, constructor);

	il.append(RETURN);

	constructor.stripAttributes(true);
	constructor.setMaxLocals();
	constructor.setMaxStack();
	classGen.addMethod(constructor.getMethod());

    }


    /**
     * Compile a topLevel() method into the output class. This method is 
     * called from transform() to handle all non-template top-level elemtents.
     * Returns the signature of the topLevel() method.
     */
    private String compileTopLevel(ClassGenerator classGen,
				   Enumeration elements) {

	final ConstantPoolGen cpg = classGen.getConstantPool();

	// The signature of this method depends on the type of DOM we're using
	final de.fub.bytecode.generic.Type[] argTypes = {
	    Util.getJCRefType(classGen.getDOMClassSig()),
	    Util.getJCRefType(NODE_ITERATOR_SIG),
	    Util.getJCRefType(TRANSLET_OUTPUT_SIG)
	};

	final String[] argNames = {
	    DOCUMENT_PNAME, ITERATOR_PNAME, TRANSLET_OUTPUT_PNAME
	};

	final InstructionList il = new InstructionList();

	final MethodGenerator toplevel =
	    new MethodGenerator(ACC_PUBLIC,
				de.fub.bytecode.generic.Type.VOID,
				argTypes, argNames,
				"topLevel", _className, il,
				classGen.getConstantPool());

	toplevel.addException("org.apache.xalan.xsltc.TransletException");

	// Define and initialize 'current' variable with the root node
	final LocalVariableGen current = 
	    toplevel.addLocalVariable("current",
				    de.fub.bytecode.generic.Type.INT,
				    il.getEnd(), null);

	final int setFilter = cpg.addMethodref(classGen.getDOMClass(),
			       "setFilter",
			       "(Lorg/apache/xalan/xsltc/dom/StripWhitespaceFilter;)V");

	il.append(new PUSH(cpg, DOM.ROOTNODE));
	il.append(new ISTORE(current.getIndex()));

	// Initialize global variables and parameterns
	final int m = _globals.size();
	for (int i = 0; i < m; i++) {
	    TopLevelElement elem = (TopLevelElement)_globals.elementAt(i);
	    elem.translate(classGen, toplevel);
	}

	Vector whitespaceRules = new Vector();

	// Compile code for other top-level elements
	while (elements.hasMoreElements()) {
	    final Object element = elements.nextElement();
	    // xsl:output
	    if (element instanceof Output) {
		((Output)element).translate(classGen, toplevel);
	    }
	    // xsl:key
	    /*
	    else if (element instanceof Key) {
		final Key key = (Key)element;
		key.translate(classGen, toplevel);
		_keys.put(key.getName(),key);
	    }
	    */
	    // xsl:decimal-format
	    else if (element instanceof DecimalFormatting) {
		((DecimalFormatting)element).translate(classGen,toplevel);
	    }
	    // xsl:strip/preserve-space
	    else if (element instanceof Whitespace) {
		whitespaceRules.addAll(((Whitespace)element).getRules());
	    }
	    // xsl:param
	    else if (element instanceof Param) {
		((Param)element).translate(classGen,toplevel);
	    }
	    // xsl:variable
	    else if (element instanceof Variable) {
		((Variable)element).translate(classGen,toplevel);
	    }
	}

	if (whitespaceRules.size() > 0) {
	    Whitespace.translateRules(whitespaceRules,classGen);
	}

	if (classGen.containsMethod("stripSpace",
				    "(Lorg/apache/xalan/xsltc/DOM;II)Z") != null) {
	    il.append(toplevel.loadDOM());
	    il.append(new ALOAD(0));
	    il.append(new INVOKEVIRTUAL(setFilter));
	}

	il.append(RETURN);

	// Compute max locals + stack and add method to class
	toplevel.stripAttributes(true);
	toplevel.setMaxLocals();
	toplevel.setMaxStack();
	toplevel.removeNOPs();

	classGen.addMethod(toplevel.getMethod());
	
	return("("+ classGen.getDOMClassSig()+
	       NODE_ITERATOR_SIG + TRANSLET_OUTPUT_SIG + ")V");
    }


    /**
     * Compile a buildKeys() method into the output class. This method is 
     * called from transform() to handle build all indexes needed by key().
     */
    private String compileBuildKeys(ClassGenerator classGen) {

	final ConstantPoolGen cpg = classGen.getConstantPool();

	// The signature of this method depends on the type of DOM we're using
	final de.fub.bytecode.generic.Type[] argTypes = {
	    Util.getJCRefType(DOM_INTF_SIG),
	    Util.getJCRefType(NODE_ITERATOR_SIG),
	    Util.getJCRefType(TRANSLET_OUTPUT_SIG),
	    de.fub.bytecode.generic.Type.INT
	};

	final String[] argNames = {
	    DOCUMENT_PNAME, ITERATOR_PNAME, TRANSLET_OUTPUT_PNAME, "current"
	};

	final InstructionList il = new InstructionList();

	final MethodGenerator buildKeys =
	    new MethodGenerator(ACC_PUBLIC,
				de.fub.bytecode.generic.Type.VOID,
				argTypes, argNames,
				"buildKeys", _className, il,
				classGen.getConstantPool());

	final int domField = cpg.addFieldref(getClassName(),
					     DOM_FIELD,
					     classGen.getDOMClassSig());

	buildKeys.addException("org.apache.xalan.xsltc.TransletException");

	il.append(classGen.loadTranslet());
	il.append(new GETFIELD(domField));  // The DOM reference
	il.append(new ASTORE(1));
	
	final Enumeration elements = elements();
	// Compile code for other top-level elements
	while (elements.hasMoreElements()) {
	    // xsl:key
	    final Object element = elements.nextElement();
	    if (element instanceof Key) {
		final Key key = (Key)element;
		key.translate(classGen, buildKeys);
		_keys.put(key.getName(),key);
	    }
	}
	
	il.append(RETURN);
	
	// Compute max locals + stack and add method to class
	buildKeys.stripAttributes(true);
	buildKeys.setMaxLocals();
	buildKeys.setMaxStack();
	buildKeys.removeNOPs();

	classGen.addMethod(buildKeys.getMethod());
	
	return("("+DOM_INTF_SIG+NODE_ITERATOR_SIG+TRANSLET_OUTPUT_SIG+"I)V");
    }

    /**
     * Compile transform() into the output class. This method is used to 
     * initialize global variables and global parameters. The current node
     * is set to be the document's root node.
     */
    private void compileTransform(ClassGenerator classGen) {
	final ConstantPoolGen cpg = classGen.getConstantPool();

	/* 
	 * Define the the method transform with the following signature:
	 * void transform(DOM, NodeIterator, HandlerBase)
	 */
	final de.fub.bytecode.generic.Type[] argTypes = 
	    new de.fub.bytecode.generic.Type[3];
	argTypes[0] = Util.getJCRefType(DOM_INTF_SIG);
	argTypes[1] = Util.getJCRefType(NODE_ITERATOR_SIG);
	argTypes[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG);

	final String[] argNames = new String[3];
	argNames[0] = DOCUMENT_PNAME;
	argNames[1] = ITERATOR_PNAME;
	argNames[2] = TRANSLET_OUTPUT_PNAME;

	final InstructionList il = new InstructionList();
	final MethodGenerator transf =
	    new MethodGenerator(ACC_PUBLIC,
				de.fub.bytecode.generic.Type.VOID,
				argTypes, argNames,
				"transform",
				_className,
				il,
				classGen.getConstantPool());
	transf.addException("org.apache.xalan.xsltc.TransletException");

	// Define and initialize current with the root node
	final LocalVariableGen current = 
	    transf.addLocalVariable("current",
				    de.fub.bytecode.generic.Type.INT,
				    il.getEnd(), null);
	final String applyTemplatesSig = classGen.getApplyTemplatesSig();
	final int applyTemplates = cpg.addMethodref(getClassName(),
						    "applyTemplates",
						    applyTemplatesSig);
	final int domField = cpg.addFieldref(getClassName(),
					     DOM_FIELD,
					     classGen.getDOMClassSig());

	// push translet for PUTFIELD
	il.append(classGen.loadTranslet());
	// prepare appropriate DOM implementation
	
	if (isMultiDOM()) {
	    il.append(new NEW(cpg.addClass(MULTI_DOM_CLASS)));
	    il.append(DUP);
	}
	
	il.append(classGen.loadTranslet());
	il.append(transf.loadDOM());
	il.append(new INVOKEVIRTUAL(cpg.addMethodref(TRANSLET_CLASS,
						     "makeDOMAdapter",
						     "("
						     + DOM_INTF_SIG
						     + ")"
						     + DOM_ADAPTER_SIG)));
	// DOMAdapter is on the stack

	if (isMultiDOM()) {
	    final int init = cpg.addMethodref(MULTI_DOM_CLASS,
					      "<init>",
					      "(" + DOM_ADAPTER_SIG + ")V");
	    il.append(new INVOKESPECIAL(init));
	    // MultiDOM is on the stack
	}
	
	//store to _dom variable
	il.append(new PUTFIELD(domField));

	// continue with globals initialization
	il.append(new PUSH(cpg, DOM.ROOTNODE));
	il.append(new ISTORE(current.getIndex()));
	
	// Look for top-level elements that need handling
	final Enumeration toplevel = elements();
	if ((_globals.size() > 0) || (toplevel.hasMoreElements())) {
	    // Compile method for handling top-level elements
	    final String topLevelSig = compileTopLevel(classGen, toplevel);
	    // Get a reference to that method
	    final int topLevelIdx = cpg.addMethodref(getClassName(),
						     "topLevel",
						     topLevelSig);
	    // Push all parameters on the stack and call topLevel()
	    il.append(classGen.loadTranslet()); // The 'this' pointer
	    il.append(classGen.loadTranslet());
	    il.append(new GETFIELD(domField));  // The DOM reference
	    il.append(transf.loadIterator());
	    il.append(transf.loadHandler());    // The output handler
	    il.append(new INVOKEVIRTUAL(topLevelIdx));
	}
	
	final String keySig = compileBuildKeys(classGen);
	final int    keyIdx = cpg.addMethodref(getClassName(),
					       "buildKeys", keySig);
	il.append(classGen.loadTranslet());     // The 'this' pointer
	il.append(classGen.loadTranslet());
	il.append(new GETFIELD(domField));      // The DOM reference
	il.append(transf.loadIterator());       // Not really used, but...
	il.append(transf.loadHandler());        // The output handler
	il.append(new PUSH(cpg, DOM.ROOTNODE)); // Start with the root node
	il.append(new INVOKEVIRTUAL(keyIdx));

	// start document
	il.append(transf.loadHandler());
	il.append(transf.startDocument());

	// push first arg for applyTemplates
	il.append(classGen.loadTranslet());
	// push translet for GETFIELD to get DOM arg
	il.append(classGen.loadTranslet());
	il.append(new GETFIELD(domField));
	// push remaining 2 args
	il.append(transf.loadIterator());
	il.append(transf.loadHandler());
	il.append(new INVOKEVIRTUAL(applyTemplates));
	// endDocument
	il.append(transf.loadHandler());
	il.append(transf.endDocument());

	il.append(RETURN);

	// Compute max locals + stack and add method to class
	transf.stripAttributes(true);
	transf.setMaxLocals();
	transf.setMaxStack();
	transf.removeNOPs();

	classGen.addMethod(transf.getMethod());
    }

    /**
     * Peephole optimization: Remove sequences of [ALOAD, POP].
     */
    private void peepHoleOptimization(MethodGenerator methodGen) {
	final String pat = "`ALOAD'`POP'`Instruction'";
	final InstructionList il = methodGen.getInstructionList();
	final FindPattern find = new FindPattern(il);

	InstructionHandle ih = find.search(pat);
	while (ih != null) {
	    InstructionHandle[] match = find.getMatch();
	    try {
		il.delete(match[0], match[1]);
	    }
	    catch (TargetLostException e) {
				// TODO: move target down into the list
	    }
	    ih = find.search(pat, match[2]);
	}
    }

    public int addParam(Param param) {
	_globals.addElement(param);
	return _globals.size() - 1;
    }

    public int addVariable(Variable global) {
	_globals.addElement(global);
	return _globals.size() - 1;
    }

    public void display(int indent) {
	indent(indent);
	Util.println("Stylesheet");
	displayContents(indent + IndentIncrement);
    }

    // do we need this wrapper ?????
    public String getNamespace(String prefix) {
	return lookupNamespace(prefix);
    }

    public String getClassName() {
	return _className;
    }

    public Vector getTemplates() {
	return _templates;
    }
}
